package top.hudk.test;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 批量处理对象中类型为 BigDecimal 的属性的精度
 *
 * @author HuDongkang
 * @date 2022-01-11 15:23:43
 */
@Slf4j
public class ScaleUtils {

    /**
     * 默认保留小数点位数
     */
    private static final int DEFAULT_NEW_SCALE = 2;

    /**
     * 取舍模式，默认为 ROUND_FLOOR
     */
    private static final int DEFAULT_ROUNDING_MODE = BigDecimal.ROUND_FLOOR;

    /**
     * 批量处理 对象中类型为 BigDecimal 的属性的小数保留位数
     * 默认保留位数为 2，并直接舍去两位小数后面的数字（不会四舍五入）
     *
     * @param target 任意对象，支持简单的对象类型，同时支持集合对象类型
     */
    public static void batchScale(Object target) {
        if (Objects.isNull(target)) {
            return;
        }
        if (target instanceof Collection) {
            batchScaleForList((Collection<?>) target);
            return;
        }
        if (target instanceof Map) {
            batchScaleForMap((Map<?, ?>) target);
            return;
        }
        batchScale(target, null, null);
    }

    /**
     * 批量处理 对象中类型为 BigDecimal 的属性的小数保留位数
     *
     * @param target       任意对象
     * @param newScale     小数点位数
     * @param roundingMode 舍入模式
     */
    public static void batchScale(Object target, Integer newScale, Integer roundingMode) {
        if (Objects.isNull(target)) {
            return;
        }
        if (target instanceof Collection) {
            batchScaleForList((Collection<?>) target, newScale, roundingMode);
            return;
        }
        if (target instanceof Map) {
            batchScaleForMap((Map<?, ?>) target, newScale, roundingMode);
            return;
        }
        //获取类对象中的所有属性对象
        List<Field> fieldList = fieldList(target.getClass());

        //过滤属性对象，只返回 属性的type 为 BigDecimal类型 的属性对象
        List<Field> fieldTargetList = fieldList.stream()
                .filter(field -> field.getType().equals(BigDecimal.class))
                .collect(Collectors.toList());

        //遍历处理每个属性
        for (Field field : fieldTargetList) {
            scaleHandleForBigDecimal(target, field, newScale, roundingMode);
        }
    }


    /**
     * 批量处理 对象中类型为 BigDecimal 的属性的小数保留位数
     * 默认保留位数为 2，并直接舍去两位小数后面的数字（不会四舍五入）
     *
     * @param targetList List类型对象
     */
    private static void batchScaleForList(Collection<?> targetList) {
        batchScaleForList(targetList, null, null);
    }

    /**
     * 批量处理 对象中类型为 BigDecimal 的属性的小数保留位数
     *
     * @param targetList   List类型对象
     * @param newScale     小数点位数
     * @param roundingMode 舍入模式
     */
    private static void batchScaleForList(Collection<?> targetList, Integer newScale, Integer roundingMode) {
        if (Objects.isNull(targetList) || targetList.isEmpty()) {
            return;
        }
        for (Object target : targetList) {
            batchScale(target, newScale, roundingMode);
        }
    }


    /**
     * 批量处理 对象中类型为 BigDecimal 的属性的小数保留位数
     * 默认保留位数为 2，并直接舍去两位小数后面的数字（不会四舍五入）
     *
     * @param targetMap Map类型对象
     */
    private static void batchScaleForMap(Map<?, ?> targetMap) {
        batchScaleForMap(targetMap, null, null);
    }

    /**
     * 批量处理 对象中类型为 BigDecimal 的属性的小数保留位数
     *
     * @param targetMap    Map类型对象
     * @param newScale     小数点位数
     * @param roundingMode 舍入模式
     */
    private static void batchScaleForMap(Map<?, ?> targetMap, Integer newScale, Integer roundingMode) {
        if (Objects.isNull(targetMap) || targetMap.isEmpty()) {
            return;
        }
        Set<? extends Map.Entry<?, ?>> entries = targetMap.entrySet();
        for (Map.Entry<?, ?> entry : entries) {
            batchScale(entry.getValue(), newScale, roundingMode);
        }
    }


    /**
     * 将目标对象的指定属性设置小数点位数
     *
     * @param target          目标对象
     * @param bigDecimalField 金额类型属性对象
     * @param newScale        小数点位数
     * @param roundingMode    取舍模式
     */
    private static void scaleHandleForBigDecimal(Object target, Field bigDecimalField, Integer newScale, Integer roundingMode) {
        if (Objects.isNull(target) || Objects.isNull(bigDecimalField)) {
            return;
        }
        if (!BigDecimal.class.equals(bigDecimalField.getType())) {
            return;
        }
        try {
            Method getMethod = getGetMethod(target.getClass(), bigDecimalField);
            if (Objects.nonNull(getMethod)) {
                Object value = getMethod.invoke(target);
                if (Objects.isNull(value)) {
                    return;
                }
                // 属性类型为BigDecimal才处理
                if (value instanceof BigDecimal) {
                    if (Objects.isNull(newScale)) {
                        newScale = DEFAULT_NEW_SCALE;
                    }
                    if (Objects.isNull(roundingMode)) {
                        roundingMode = DEFAULT_ROUNDING_MODE;
                    }
                    //保留两位小数
                    value = ((BigDecimal) value).setScale(newScale, roundingMode);
                }
                Method setMethod = getSetMethod(target.getClass(), bigDecimalField);
                if (Objects.nonNull(setMethod)) {
                    setMethod.invoke(target, value);
                }
            }
        } catch (Exception e) {
            log.error("处理字段{}小数保留位数失败,异常信息：{}", bigDecimalField.getName(), e.getMessage());
        }
    }

    /**
     * 获取类对象中的所有属性对象
     *
     * @param tempClass 类对象
     * @return 属性对象列表
     */
    private static List<Field> fieldList(Class<?> tempClass) {
        List<Field> fieldList = new ArrayList<>();
        //迭代父类获取所有属性
        while (Objects.nonNull(tempClass) && !Object.class.getName().equalsIgnoreCase(tempClass.getName())) {
            //获取实例的所有属性
            fieldList.addAll(Arrays.asList(tempClass.getDeclaredFields()));
            //迭代父类
            tempClass = tempClass.getSuperclass();
        }
        return fieldList;
    }

    /**
     * 获取字段对应的 set 方法
     *
     * @param objectClass 类对象
     * @param field       属性对象
     * @return 方法对象
     */
    private static Method getSetMethod(Class<?> objectClass, Field field) {
        String fieldName = field.getName();
        try {
            //组织set方法名称
            String methodName = "set" +
                    fieldName.substring(0, 1).toUpperCase() +
                    fieldName.substring(1);
            //获取set方法对象
            return objectClass.getMethod(methodName, field.getType());
        } catch (Exception e) {
            log.error("[反射]获取对象{}的{}方法失败,异常信息:{}", objectClass.getName(), fieldName, e.getMessage());
        }
        return null;
    }

    /**
     * 获取字段对应的 get 方法
     *
     * @param objectClass 类对象
     * @param field       属性对象
     * @return 方法对象
     */
    private static Method getGetMethod(Class<?> objectClass, Field field) {
        String fieldName = field.getName();
        try {
            String methodName = "get" +
                    fieldName.substring(0, 1).toUpperCase() +
                    fieldName.substring(1);
            return objectClass.getMethod(methodName);
        } catch (Exception e) {
            log.error("[反射]获取对象{}的{}方法失败,异常信息:{}", objectClass.getName(), fieldName, e.getMessage());
        }
        return null;
    }
}
